#include	"../include/Scheduler.h"
#include	"../include/OS.h"

Scheduler::Scheduler()
	: _alloc_id(0)
	, _next(-1)
	, _is_breathing(false)
	, _tasks() {}

Scheduler & Scheduler::Instance() {
	static Scheduler * g_scheduler = nullptr;
	if (g_scheduler == nullptr) g_scheduler = new Scheduler();
	return *g_scheduler;
}

uint64_t Scheduler::Add(double delay, const Job & job, bool loop) {
	return __Add(OS::Tick() + delay, loop ? delay : 0, job);
}

uint64_t Scheduler::Add(int hour, int minute, int second, const Job & job) {
	DateTime now = OS::Now();
	DateTime today = OS::ParseDateTime(now.year, now.month, now.day, hour, minute, second);
	
	double delay = today - now;
	if (delay < 0) delay += 24 * 3600 * 1000;
	return __Add(OS::Tick() + delay, 24 * 3600 * 1000, job);
}

bool Scheduler::IsValid(uint64_t id) {
	for (auto p : _tasks) {
		if (p->id == id) return !(p->cancelled);
	}
	return false;
}

double Scheduler::GetRemainTime(uint64_t id) {
	for (auto p : _tasks) {
		if (p->id == id) return p->cancelled ? -1 : (p->trigger - OS::Tick());
	}
	return -1;
}

void Scheduler::Cancel(uint64_t id) {
	for (auto p : _tasks) {
		if (p->id == id) {
			p->cancelled = true;
			break;
		}
	}
}

void Scheduler::OnBreath() {
	double tick = OS::Tick();
	if (_next < 0 || _next > tick) return;

	bool need_sort = false;
	for (auto it = _tasks.begin(); it != _tasks.end();) {
		Task * task = *it;
		if (task->trigger > tick) break;

		it = _tasks.erase(it);
		if (task->cancelled) {
			delete task;
			continue;
		}

		task->job(task->id);
		if (task->loop_delay > 0) {
			task->trigger += task->loop_delay;
			_tasks.push_back(task);
			need_sort = true;
		} else {
			delete task;
		}
	}

	if (need_sort) _tasks.sort([](Task * l, Task * r) { return l->trigger < r->trigger; });
	_next = _tasks.size() > 0 ? _tasks.front()->trigger : -1;
}

uint64_t Scheduler::__Add(double trigger, double loop, const Job & job) {
	Task * task = new Task();
	task->id = ++_alloc_id;
	task->cancelled = false;
	task->trigger = trigger;
	task->loop_delay = loop;
	task->job = job;

	_tasks.push_back(task);
	_tasks.sort([](Task * l, Task * r) { return l->trigger < r->trigger; });
	_next = _tasks.front()->trigger;

	return task->id;
}